using System;
using System.Collections.Generic;
using System.Text;

namespace org.ngwt.user.server.rpc
{
    using System.Web;
    using System.IO;



   /**
    * Utility class containing helper methods used by servlets(HttpHandler) that integrate with
    * the RPC system.
    */
    public class RPCServletUtils
    {
        private static String ACCEPT_ENCODING = "Accept-Encoding";

        //private static String CHARSET_UTF8 = "UTF-8";

        //private static String CONTENT_ENCODING = "Content-Encoding";

        private static String CONTENT_ENCODING_GZIP = "gzip";

        private static String CONTENT_TYPE_TEXT_PLAIN_UTF8 = "text/plain; charset=utf-8";

        private static String GENERIC_FAILURE_MSG = "The call failed on the server; see server log for details";

        /**
         * Controls the compression threshold at and below which no compression will
         * take place.
         */
        private static int UNCOMPRESSED_BYTE_SIZE_LIMIT = 256;

        /**
         * Returns <code>true</code> if the {@link HttpRequest} accepts Gzip
         * encoding. This is done by checking that the accept-encoding header
         * specifies gzip as a supported encoding.
         * 
         * @param request the request instance to test for gzip encoding acceptance
         * @return <code>true</code> if the {@link HttpRequest} accepts Gzip
         *         encoding
         */
        public static Boolean acceptsGzipEncoding(HttpRequest request)
        {
            //assert(request != null);

            String acceptEncoding = request.Headers[ACCEPT_ENCODING];
            if (null == acceptEncoding)
            {
                return false;
            }

            return (acceptEncoding.IndexOf(CONTENT_ENCODING_GZIP) != -1);
        }

        /**
         * Returns <code>true</code> if the response content's estimated UTF-8 byte
         * length exceeds 256 bytes.
         * 
         * @param content the contents of the response
         * @return <code>true</code> if the response content's estimated UTF-8 byte
         *         length exceeds 256 bytes
         */
        public static Boolean exceedsUncompressedContentLengthLimit(String content)
        {
            return (content.Length * 2) > UNCOMPRESSED_BYTE_SIZE_LIMIT;
        }

        /**
         * Returns the content of an {@link HttpServletRequest} by decoding it using
         * the UTF-8 charset.
         * 
         * @param request the servlet request whose content we want to read
         * @return the content of an {@link HttpServletRequest} by decoding it using
         *         the UTF-8 charset
         * @throws IOException if the requests input stream cannot be accessed, read
         *           from or closed
         * @throws ServletException if the content length of the request is not
         *           specified of if the request's content type is not 'text/plain' or
         *           'charset=utf-8'
         */
        public static String readContentAsUtf8(HttpRequest request) /*throws IOException, ServletException */
        {

            int contentLength = request.ContentLength;
            if (contentLength == -1)
            {
                // Content length must be known.
                throw new HttpException("Content-Length must be specified");
            }



            String contentType = request.ContentType;
            Boolean contentTypeIsOkay = false;
            // Content-Type must be specified.
            if (contentType != null)
            {
                contentType = contentType.ToLower();
                // The type must be plain text.
                if (contentType.StartsWith("text/plain"))
                {
                    // And it must be UTF-8 encoded (or unspecified, in which case we assume
                    // that it's either UTF-8 or ASCII).
                    if (contentType.IndexOf("charset=") == -1)
                    {
                        contentTypeIsOkay = true;
                    }
                    else if (contentType.IndexOf("charset=utf-8") != -1)
                    {
                        contentTypeIsOkay = true;
                    }
                }
            }


            if (!contentTypeIsOkay)
            {
                throw new HttpException(
                  "Content-Type must be 'text/plain' with 'charset=utf-8' (or unspecified charset)");
            }

            Stream sin = request.InputStream;

            try
            {
                byte[] payload = new byte[contentLength];
                int offset = 0;
                int len = contentLength;
                int byteCount;
                while (offset < contentLength)
                {
                    byteCount = sin.Read(payload, offset, len);
                    if (byteCount == -1)
                    {
                        throw new HttpException("Client did not send " + contentLength
                            + " bytes as expected");
                    }
                    offset += byteCount;
                    len -= byteCount;
                }

                //StringBuilder sb = new StringBuilder();
                //for (int i = 0; i < contentLength; i++)
                //{
                //    sb.Append(payload[i]);
                //}
                

                //sbyte[] spayload = new sbyte[contentLength];
                //for(int i=0;i<contentLength;i++)
                //{
                //    spayload[i] = (sbyte)payload[i];
                //}
                //byte[] sep = new byte[] { 191, 191 };
                //String sepStr = Encoding.UTF7.GetString(sep);
                String outStr = null;
                outStr = Encoding.UTF7.GetString(payload);
                //String repfrom = outStr.Substring(1, 3);
                //string repto = outStr.Substring(1, 1);
                //outStr = outStr.Replace(repfrom, repto);
                //outStr = sb.ToString();
                //unsafe
                //{
                //    UTF8Encoding encoding = new UTF8Encoding(true, true);

                //    // Instruct the Garbage Collector not to move the memory
                //    fixed (sbyte* pAsciiChars = spayload)
                //    {
                //        outStr = new String(pAsciiChars, 0, payload.Length, encoding);
                //    }
                //}

                return outStr;

            }
            finally
            {
                if (sin != null)
                {
                    sin.Close();
                }
                //return "";
            }

        }

        /**
         * Returns <code>true</code> if the request accepts gzip encoding and the
         * the response content's estimated UTF-8 byte length exceeds 256 bytes.
         * 
         * @param request the request associated with the response content
         * @param responseContent a string that will be
         * @return <code>true</code> if the request accepts gzip encoding and the
         *         the response content's estimated UTF-8 byte length exceeds 256
         *         bytes
         */
        public static Boolean shouldGzipResponseContent(HttpRequest request,String responseContent)
        {
            return acceptsGzipEncoding(request)
                && exceedsUncompressedContentLengthLimit(responseContent);
        }

      /**
       * Write the response content into the {@link HttpServletResponse}. If
       * <code>gzipResponse</code> is <code>true</code>, the response content
       * will be gzipped prior to being written into the response.
       * 
       * @param servletContext servlet context for this response
       * @param response response instance
       * @param responseContent a string containing the response content
       * @param gzipResponse if <code>true</code> the response content will be
       *          gzip encoded before being written into the response
       * @throws IOException if reading, writing, or closing the response's output
       *           stream fails
       */
      public static void writeResponse(HttpContext servletContext,
          HttpResponse response, String responseContent, Boolean gzipResponse)
      {

          /*
        byte[] responseBytes = responseContent.getBytes(CHARSET_UTF8);
        if (gzipResponse) {
          // Compress the reply and adjust headers.
          //
          ByteArrayOutputStream output = null;
          GZIPOutputStream gzipOutputStream = null;
          Throwable caught = null;
          try {
            output = new ByteArrayOutputStream(responseBytes.length);
            gzipOutputStream = new GZIPOutputStream(output);
            gzipOutputStream.write(responseBytes);
            gzipOutputStream.finish();
            gzipOutputStream.flush();
            response.setHeader(CONTENT_ENCODING, CONTENT_ENCODING_GZIP);
            responseBytes = output.toByteArray();
          } catch (IOException e) {
            caught = e;
          } finally {
            if (null != gzipOutputStream) {
              gzipOutputStream.close();
            }
            if (null != output) {
              output.close();
            }
          }

          if (caught != null) {
            servletContext.log("Unable to compress response", caught);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
          }
        }
           */
          StreamWriter writer = null;
          //System.IO.Compression.GZipStream gzipOutputStream = null;
          //if (false/*gzipResponse*/)
          //{
          //    gzipOutputStream = new System.IO.Compression.GZipStream(response.OutputStream
          //        , System.IO.Compression.CompressionMode.Compress, true);

          //    writer = new StreamWriter(gzipOutputStream);
          //    response.Headers[CONTENT_ENCODING] = CONTENT_ENCODING_GZIP;
          //}
          //else
          //{
               writer = new StreamWriter(response.OutputStream);
          //}

        // Send the reply.
        //
       // response.setContentLength(responseBytes.length);
        response.ContentType= CONTENT_TYPE_TEXT_PLAIN_UTF8;
        //response.setStatus(HttpServletResponse.SC_OK);
        response.StatusCode = 200;
        response.StatusDescription = "OK";
        //response.getOutputStream().write(responseBytes);
        
        writer.Write(responseContent);
        writer.Close();
        //if(gzipOutputStream !=null)
        //{
        //    gzipOutputStream.Flush();
        //    gzipOutputStream.Close();
        //}
      }

      /**
       * Called when the servlet itself has a problem, rather than the invoked
       * third-party method. It writes a simple 500 message back to the client.
       * 
       * @param servletContext
       * @param response
       * @param failure
       */
      public static void writeResponseForUnexpectedFailure(
          HttpContext servletContext, HttpResponse response,
          Exception failure)
      {
          //servletContext.log("Exception while dispatching incoming RPC call", failure);

          // Send GENERIC_FAILURE_MSG with 500 status.
          //
          try
          {
              response.ContentType = "text/plain";
              //response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
              response.StatusCode = 500;
              response.StatusDescription = "INTERNAL_SERVER_ERROR";
              //response.getWriter().write(GENERIC_FAILURE_MSG);
              StreamWriter writer = new StreamWriter(response.OutputStream);
              writer.Write(GENERIC_FAILURE_MSG);
              writer.Close();
          }
          catch (IOException )
          {
              //servletContext.log(
              //    "respondWithUnexpectedFailure failed while sending the previous failure to the client",
              //    ex);
          }
      }

      private RPCServletUtils()
      {
          // Not instantiable
      }

    }
}
